package com.shockweb.common.serializable.binary;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.shockweb.common.International;
import com.shockweb.common.serializable.SerializableObject;
import com.shockweb.common.utils.ClassUtil;
import com.shockweb.common.utils.classmethod.ClassMethod;
import com.shockweb.common.utils.classmethod.ClassMethodDefine;
/**
 * 将二进制数据转化成对象
 * 
 * @author 彭明华
 * 2017年12月19日 创建
 */
public class BinaryReader {
	/**
	 * 预演J表似java
	 */
	public static final byte LANUAGE_JAVA = 'j';

	/**
	 * 空对象
	 */
	public static final byte TYPE_NULL = 81;
	/**
	 * Object对象
	 */
	public static final byte TYPE_OBJECT = 82;
	/**
	 * String对象
	 */
	public static final byte TYPE_STRING = 83;
	/**
	 * StringBuffer对象
	 */
	public static final byte TYPE_STRINGBUFFER = 84;
	/**
	 * StringBuilder对象
	 */
	public static final byte TYPE_STRINGBUILDER = 85;
	/**
	 * BigDecimal对象
	 */
	public static final byte TYPE_BIGDECIMAL = 86;
	/**
	 * Double对象
	 */
	public static final byte TYPE_DOUBLE = 87;
	/**
	 * double数值
	 */
	public static final byte TYPE_double = 88;
	/**
	 * Float对象
	 */
	public static final byte TYPE_FLOAT = 89;
	/**
	 * float数值
	 */
	public static final byte TYPE_float = 90;
	/**
	 * Integer对象
	 */
	public static final byte TYPE_INTEGER = 91;
	/**
	 * int数值
	 */
	public static final byte TYPE_int = 92;
	/**
	 * Long对象
	 */
	public static final byte TYPE_LONG = 93;
	/**
	 * long数值
	 */
	public static final byte TYPE_long = 94;
	/**
	 * Short对象
	 */
	public static final byte TYPE_SHORT = 95;
	/**
	 * short数值
	 */
	public static final byte TYPE_short = 96;
	/**
	 * Byte对象
	 */
	public static final byte TYPE_BYTE = 97;
	/**
	 * byte数值
	 */
	public static final byte TYPE_byte = 98;
	/**
	 * Character对象
	 */
	public static final byte TYPE_CHARACTER = 99;
	/**
	 * char数值
	 */
	public static final byte TYPE_char = 100;
	/**
	 * Date对象
	 */
	public static final byte TYPE_DATE = 101;
	/**
	 * java.sql.Date对象
	 */
	public static final byte TYPE_SQLDATE = 102;
	/**
	 * Time对象
	 */
	public static final byte TYPE_TIME = 103;
	/**
	 * TimeStamp对象
	 */
	public static final byte TYPE_TIMESTAMP = 104;
	/**
	 * Boolean对象
	 */
	public static final byte TYPE_BOOLEAN = 105;
	/**
	 * boolean数值
	 */
	public static final byte TYPE_boolean = 106;
	/**
	 * BigInteger对象
	 */
	public static final byte TYPE_BIGINTEGER = 107;
	/**
	 * class对象
	 */
	public static final byte TYPE_CLASS = 108;
	/**
	 * 自定义对象
	 */
	public static final byte TYPE_CUSTOM = 80;
	/**
	 * 数组对象
	 */
	public static final byte TYPE_ARRAY = 121;
	/**
	 * Map对象
	 */
	public static final byte TYPE_MAP = 122;
	/**
	 * List对象
	 */
	public static final byte TYPE_LIST = 123;

	/**
	 * 空对象的长度
	 */
	public static final short LEN_NULL = 0;
	/**
	 * integer的长度
	 */
	public static final short LEN_INTEGER = 4;
	/**
	 * Long的的长度
	 */
	public static final short LEN_LONG = 8;
	/**
	 * short的长度
	 */
	public static final short LEN_SHORT = 2;
	/**
	 * double的长度
	 */
	public static final short LEN_DOUBLE = 8;
	/**
	 * float的长度
	 */
	public static final short LEN_FLOAT = 4;
	/**
	 * Byte的长度
	 */
	public static final short LEN_BYTE = 1;
	/**
	 * char的长度
	 */
	public static final short LEN_CHAR = 2;
	/**
	 * date的长度
	 */
	public static final short LEN_DATE = 8;
	/**
	 * Time的长度
	 */
	public static final short LEN_TIME = 8;
	/**
	 * TimeStamp的长度
	 */
	public static final short LEN_TIMESTAMP = 8;
	/**
	 * boolean的长度
	 */
	public static final short LEN_BOOLEAN = 1;
	
	/**
	 * boolean的长度
	 */
	public static final short LEN_CLASS = 1;

	/**
	 * 获取type对应的class
	 * @param type
	 * @return Class
	 */
	public static Class<?> getClass(byte type){
		switch(type){
			case TYPE_STRING:
				return String.class;
			case TYPE_INTEGER:
				return Integer.class;
			case TYPE_int:
				return int.class;
			case TYPE_LONG:
				return Long.class;
			case TYPE_long:
				return long.class;
			case TYPE_BIGDECIMAL:
				return BigDecimal.class;
			case TYPE_DOUBLE:
				return Double.class;
			case TYPE_double:
				return double.class;
			case TYPE_FLOAT:
				return Float.class;
			case TYPE_float:
				return float.class;
			case TYPE_SHORT:
				return Short.class;
			case TYPE_short:
				return short.class;
			case TYPE_BYTE:
				return Byte.class;
			case TYPE_byte:
				return byte.class;
			case TYPE_CHARACTER:
				return Character.class;
			case TYPE_char:
				return char.class;
			case TYPE_STRINGBUFFER:
				return StringBuffer.class;
			case TYPE_STRINGBUILDER:
				return StringBuilder.class;
			case TYPE_OBJECT:
				return Object.class;
			case TYPE_DATE:
				return Date.class;
			case TYPE_SQLDATE:
				return java.sql.Date.class;
			case TYPE_TIME:
				return Time.class;
			case TYPE_TIMESTAMP:
				return Timestamp.class;
			case TYPE_BOOLEAN:
				return Boolean.class;
			case TYPE_boolean:
				return boolean.class;
			case TYPE_BIGINTEGER:
				return BigInteger.class;
			case TYPE_CLASS:
				return Class.class;
			default:
				return null;
		}
	}

    /**
     * 返回type对应的数据长度，小于0为非固定长度
     * @param type
     * @return int
     */
    public static int typeLen(byte type){
    	switch(type){
    		case TYPE_NULL:
    			return 0;
    		case TYPE_STRING:
    			return -2;
	        case TYPE_int:
	        	return LEN_INTEGER;
	        case TYPE_INTEGER:
	        	return LEN_INTEGER;
	        case TYPE_long:
	        	return LEN_LONG;
	        case TYPE_LONG:
	        	return LEN_LONG;
    		case TYPE_BIGDECIMAL:
    			return -2;
    		case TYPE_double:
    			return LEN_DOUBLE;
    		case TYPE_DOUBLE:
    			return LEN_DOUBLE;
	        case TYPE_float:
	        	return LEN_FLOAT;
	        case TYPE_FLOAT:
	        	return LEN_FLOAT;
	        case TYPE_short:
	        	return LEN_SHORT;
	        case TYPE_SHORT:
	        	return LEN_SHORT;
	        case TYPE_byte:
	        	return LEN_BYTE;
	        case TYPE_BYTE:
	        	return LEN_BYTE;
	        case TYPE_char:
	        	return LEN_CHAR;
	        case TYPE_CHARACTER:
	        	return LEN_CHAR;
	        case TYPE_DATE:
	        	return LEN_DATE;
	        case TYPE_SQLDATE:
	        	return LEN_DATE;
	        case TYPE_TIME:
	        	return LEN_TIME;
	        case TYPE_TIMESTAMP:
	        	return LEN_TIMESTAMP;
	        case TYPE_boolean:
	        	return LEN_BOOLEAN;
	        case TYPE_BOOLEAN:
	        	return LEN_BOOLEAN;
	        case TYPE_BIGINTEGER:
	        	return -2;
    		case TYPE_STRINGBUFFER:
    			return -2;
    		case TYPE_STRINGBUILDER:
    			return -2;
    		case TYPE_CLASS:
    			return LEN_CLASS;
	        case TYPE_LIST:
	        	return -1;
	        case TYPE_MAP:
	        	return -1;
	        case TYPE_OBJECT:
	        	return -1;
	        case TYPE_ARRAY:
	        	return -1;
	        case TYPE_CUSTOM:
	        	return -1;
	        default:
	        	throw new BinaryException("非法的数据类型type=" + type);
    	}

    }
	

    /**
     * 缓存当前使用的class定义
     */
    private Map<Byte,String> classNameDefines = new HashMap<Byte,String>();
    
    /**
     * 缓存当前使用的属性名定义
     */
    private Map<Short,String> nameDefines = new HashMap<Short,String>();
    
    
    /**
     * 将byte[]转化成对象
     * @param bytes
     * @return Object
     * @throws BinaryException
     */
	public Object readBinary(byte[] bytes) throws BinaryException{
		return readBinary(bytes,0);
    }

    /**
     * 将byte[]转化成对象
     * @param bytes
     * @param offset
     * @return Object
     * @throws BinaryException
     */
	public Object readBinary(byte[] bytes,int offset) throws BinaryException{
		try{
    		classNameDefines.clear();
    		nameDefines.clear();
			byte language = bytes[offset];
			offset += LEN_BYTE;
			if(LANUAGE_JAVA!=language){
				throw new BinaryException("非JAVA语言格式");
			}
			int size = SerializableObject.bytesToInt(bytes,offset);
	    	offset += LEN_INTEGER;
	    	if(size>0){
		    	String classNames = SerializableObject.bytesToString(bytes, offset, size, International.CHARSET);
		    	offset += size;
		    	byte i=Byte.MIN_VALUE;
		    	for(String className:classNames.split(",")){
		    		classNameDefines.put(i, className);
		    		i++;
		    	}
	    	}
	    	size = SerializableObject.bytesToInt(bytes,offset);
	    	offset += LEN_INTEGER;
	    	if(size>0){
		    	String names = SerializableObject.bytesToString(bytes, offset, size, International.CHARSET);
		    	offset += size;
		    	short i = Short.MIN_VALUE;
		    	for(String name:names.split(",")){
		    		nameDefines.put(i, name);
		    		i++;
		    	}
	    	}
			byte type = bytes[offset];
			offset += LEN_BYTE;
			int tmp = SerializableObject.bytesToInt(bytes,offset);
			offset += LEN_INTEGER;
	        return readBinary(type,bytes,offset,tmp);
		}catch(BinaryException e){
			throw e;
		}catch(Exception e){
			throw new BinaryException(e);
		}
    }
	
    /**
     * 把指定范围byte[]数据转化成对象
     * @param type
     * @param bytes
     * @param offset
     * @param len
     * @return
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IOException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
	private Object readBinary(byte type,byte[] bytes,int offset,int len) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException, IllegalArgumentException, InvocationTargetException{
		if(bytes==null){
			return null;
		}
		if(type<=TYPE_CUSTOM){
			return readCustomClass(bytes,offset,len);
		}else{
	    	switch(type){
				case BinaryReader.TYPE_LIST:
					return readList(bytes,offset,len);
				case BinaryReader.TYPE_MAP:
					return readMap(bytes,offset,len);
				case BinaryReader.TYPE_ARRAY:
					return readArray(bytes,offset,len);
				default:
	    			return readValue(type,bytes,offset,len);
	    	}
		}
    }
	

   /**
    * 转化Map对象
    * @param bytes
    * @param offset
    * @param len
    * @return
    * @throws ClassNotFoundException
    * @throws InstantiationException
    * @throws IllegalAccessException
    * @throws IOException
    * @throws IllegalArgumentException
    * @throws InvocationTargetException
    */
	@SuppressWarnings({ "rawtypes", "unchecked" })
    private Object readMap(byte[] bytes,int offset,int len) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException, IllegalArgumentException, InvocationTargetException{
		int start = offset;
    	byte classNameIndex = bytes[offset];
    	offset += LEN_BYTE;
        String className = classNameDefines.get(classNameIndex);
        Class<?> classDefine = ClassUtil.getClass(className);
		Map rtn = (Map)classDefine.newInstance();
    	while(offset<start+len){
        	byte type = bytes[offset];
        	offset += LEN_BYTE;
        	int typeLen = typeLen(type);
        	Object key;
			if(typeLen==-2){
	    		int keyLen = SerializableObject.bytesToInt(bytes,offset);
	    		offset += LEN_INTEGER;
	    		key = readValue(type,bytes,offset,keyLen);
	    		offset += keyLen;
			}else if(typeLen==-1){
	    		int keyLen = SerializableObject.bytesToInt(bytes,offset);
	    		offset += LEN_INTEGER;
	    		key = readBinary(type,bytes,offset,keyLen);
	    		offset += keyLen;
			}else{
				key = readValue(type,bytes,offset,0);
	    		offset += typeLen;
			}
        	
        	byte valueType = bytes[offset];
        	offset += LEN_BYTE;
        	int valueTypeLen = typeLen(valueType);
        	Object value;
			if(valueTypeLen==-2){
	    		int valueLen = SerializableObject.bytesToInt(bytes,offset);
	    		offset += LEN_INTEGER;
	    		value = readValue(valueType,bytes,offset,valueLen);
	    		offset += valueLen;
			}else if(valueTypeLen==-1){
	    		int valueLen = SerializableObject.bytesToInt(bytes,offset);
	    		offset += LEN_INTEGER;
	    		value = readBinary(valueType,bytes,offset,valueLen);
	    		offset += valueLen;
			}else{
	    		value = readValue(valueType,bytes,offset,0);
	    		offset += valueTypeLen;
			}
    		rtn.put(key,value);
        }
    	return rtn;
    }
	    
    /**
     * 转化List对象
     * @param bytes
     * @param offset
     * @param len
     * @return
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IOException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private Object readList(byte[] bytes,int offset,int len) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException, IllegalArgumentException, InvocationTargetException{
		int start = offset;
    	byte classNameIndex = bytes[offset];
    	offset += LEN_BYTE;
        String className = classNameDefines.get(classNameIndex);
		Class<?> classDefine = ClassUtil.getClass(className);
		List rtn = (List)classDefine.newInstance();
    	while(offset<start+len){
        	byte type = bytes[offset];
        	offset += LEN_BYTE;
        	int typeLen = typeLen(type);
			if(typeLen==-2){
	    		int valueLen = SerializableObject.bytesToInt(bytes,offset);
	    		offset += LEN_INTEGER;
	    		Object value = readValue(type,bytes,offset,valueLen);
	    		offset += valueLen;
	    		rtn.add(value);
			}else if(typeLen==-1){
	    		int valueLen = SerializableObject.bytesToInt(bytes,offset);
	    		offset += LEN_INTEGER;
	    		Object value = readBinary(type,bytes,offset,valueLen);
	    		offset += valueLen;
	    		rtn.add(value);
			}else{
	    		Object value = readValue(type,bytes,offset,0);
	    		offset += typeLen;
	    		rtn.add(value);
			}
        }
    	return rtn;
    	
    }
	

    /**
     * 转化数组对象
     * @param bytes
     * @param offset
     * @param len
     * @return
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @throws IOException
     */
    private Object readArray(byte[] bytes,int offset,int len) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException{
		byte dimensions = bytes[offset];
		offset += LEN_BYTE;
		int size = SerializableObject.bytesToInt(bytes,offset);
		offset += LEN_INTEGER;
		byte type = bytes[offset];
		offset += LEN_BYTE;
		int typeLen = -1;
		if(type<0){
			typeLen = -1;
		}else{
			typeLen = typeLen(type);
		}
    	Class<?> clazz = getClass(type);
    	if(type<BinaryReader.TYPE_CUSTOM){
            clazz = ClassUtil.getClass(classNameDefines.get(type));
    	}
    	int[] dim = new int[dimensions];
    	dim[0] = size;
    	Object rtn = Array.newInstance(clazz,dim);
    	for(int i=0;i<size;i++){
    		Object value = null;
    		if(dimensions>1){
	    		byte t = bytes[offset];
	    		offset += LEN_BYTE;
	    		int valueLen = SerializableObject.bytesToInt(bytes,offset);
	    		offset += LEN_INTEGER;
	    		value = readBinary(t,bytes,offset,valueLen);
	    		offset += valueLen;
    		}else{
    			if(typeLen==-2){
		    		int valueLen = SerializableObject.bytesToInt(bytes,offset);
		    		offset += LEN_INTEGER;
		    		value = readValue(type,bytes,offset,valueLen);
		    		offset += valueLen;
    			}else if(typeLen==-1){
		    		byte t = bytes[offset];
		    		offset += LEN_BYTE;
		    		int l = typeLen(t);
		    		if(l==-2){
			    		int valueLen = SerializableObject.bytesToInt(bytes,offset);
			    		offset += LEN_INTEGER;
			    		value = readValue(t,bytes,offset,valueLen);
			    		offset += valueLen;
		    		}else if(l==-1){
			    		int valueLen = SerializableObject.bytesToInt(bytes,offset);
			    		offset += LEN_INTEGER;
			    		value = readBinary(t,bytes,offset,valueLen);
			    		offset += valueLen;
		    		}else{
		    			value = readValue(t,bytes,offset,0);
		    			offset += l;
		    		}
    			}else{
		    		value = readValue(type,bytes,offset,0);
		    		offset += typeLen;
    			}
    		}
    		Array.set(rtn, i, value);
    	}
    	return rtn;
    }
    
    /**
     * 转化自定义对象
     * @param bytes
     * @param offset
     * @param len
     * @return
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @throws IOException
     */
    private Object readCustomClass(byte[] bytes,int offset,int len) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, IOException{
    	int start = offset;
    	byte classNameIndex = bytes[offset];
    	offset += LEN_BYTE;

        String className = classNameDefines.get(classNameIndex);
        Object rtn = null;
        Map<String,ClassMethodDefine> methods = null;
		Class<?> classDefine = ClassUtil.getClass(className);
		methods = ClassMethod.getSetMethodsMap(classDefine);
		rtn = classDefine.newInstance();
    	while(offset<start+len){
    		short method = SerializableObject.bytesToShort(bytes,offset);
    		offset += LEN_SHORT;
    		String methodName = nameDefines.get(method);

        	byte type = bytes[offset];
        	offset += LEN_BYTE;
        	int typeLen = typeLen(type);
        	Object value;
			if(typeLen==-2){
	    		int valueLen = SerializableObject.bytesToInt(bytes,offset);
	    		offset += LEN_INTEGER;
	    		value = readValue(type,bytes,offset,valueLen);
	    		offset += valueLen;
			}else if(typeLen==-1){
	    		int valueLen = SerializableObject.bytesToInt(bytes,offset);
	    		offset += LEN_INTEGER;
	    		value = readBinary(type,bytes,offset,valueLen);
	    		offset += valueLen;
			}else{
	    		value = readValue(type,bytes,offset,0);
	    		offset += typeLen;
			}
    		ClassMethodDefine setMethod = methods.get(methodName);
    		if(setMethod!=null){
        		Method m = setMethod.getMethod();
        		m.invoke(rtn, value);
    		}
    	}
        return rtn;
    }
    

    
    /**
     * 转化java基本数值和对象
     * @param type
     * @param bytes
     * @param offset
     * @param len
     * @return
     * @throws ClassNotFoundException
     * @throws IOException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    private Object readValue(byte type,byte[] bytes, int offset,int len) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
    	switch(type){
	    	case TYPE_NULL:
	        	return null;
	        case TYPE_STRING:
	        	return new String(bytes,offset,len,International.CHARSET);
	        case TYPE_int:
	        	return SerializableObject.bytesToInt(bytes,offset);
	        case TYPE_INTEGER:
	        	return new Integer(SerializableObject.bytesToInt(bytes,offset));
	        case TYPE_long:
	        	return SerializableObject.bytesToLong(bytes,offset);
	        case TYPE_LONG:
	        	return new Long(SerializableObject.bytesToLong(bytes,offset));
	        case TYPE_STRINGBUFFER:
	        	return new StringBuffer(new String(bytes,offset,len,International.CHARSET));
	        case TYPE_STRINGBUILDER:
	        	return new StringBuilder(new String(bytes,offset,len,International.CHARSET));
	        case TYPE_BIGDECIMAL:
	        	return new BigDecimal(new String(bytes,offset,len,International.CHARSET));
	        case TYPE_double:
	        	return new Double(SerializableObject.bytesToDouble(bytes,offset));
	        case TYPE_DOUBLE:
	        	return SerializableObject.bytesToDouble(bytes,offset);
	        case TYPE_float:
	        	return new Float(SerializableObject.bytesToFloat(bytes,offset));
	        case TYPE_FLOAT:
	        	return SerializableObject.bytesToFloat(bytes,offset);
	        case TYPE_short:
	        	return SerializableObject.bytesToShort(bytes,offset);
	        case TYPE_SHORT:
	        	return new Short(SerializableObject.bytesToShort(bytes,offset));
	        case TYPE_byte:
	        	return bytes[offset];
	        case TYPE_BYTE:
	        	return new Byte(bytes[offset]);
	        case TYPE_char:
	        	return SerializableObject.bytesToChar(bytes, offset);
	        case TYPE_CHARACTER:
	        	return new Character(SerializableObject.bytesToChar(bytes, offset));
	        case TYPE_DATE:
	        	return SerializableObject.bytesToDate(bytes,offset);
	        case TYPE_SQLDATE:
	        	return SerializableObject.bytesToSqlDate(bytes,offset);
	        case TYPE_TIME:
	        	return SerializableObject.bytesToTime(bytes,offset);
	        case TYPE_TIMESTAMP:
	        	return SerializableObject.bytesToTimestamp(bytes,offset);
	        case TYPE_boolean:
	        	return SerializableObject.bytesToBoolean(bytes,offset);
	        case TYPE_BOOLEAN:
	        	return new Boolean(SerializableObject.bytesToBoolean(bytes,offset));
	        case TYPE_BIGINTEGER:
	        	return new BigInteger(new String(bytes,offset,len,International.CHARSET));
	        case TYPE_CLASS:
	        	return getClass(bytes,offset,len);
	        case TYPE_OBJECT:
	        	return readBinary(type, bytes, offset, len);
	        default:
	        	throw new BinaryException("非法的数据类型type" + type);
        }
    }
    
	
	/**
	 * 获取clazz的对应的class
	 * @param bytes
	 * @param offset
	 * @param len
	 * @return Class
	 * @throws ClassNotFoundException
	 * @throws UnsupportedEncodingException 
	 */
	public Class<?> getClass(byte[] bytes,int offset,int len)throws ClassNotFoundException, UnsupportedEncodingException{
		byte type = bytes[offset];
		Class<?> clazz = getClass(type);
		if(clazz==null){
			String className = classNameDefines.get(type);
			return ClassUtil.getClass(className);
		}else{
			return clazz;
		}
	}
}
